在前面幾天,我們探討了記憶體、閉包、高階函數、非同步與 Event Loop。今天我們要來拆解 React 開發者每天都在寫、卻最容易被誤解的語法——JSX。
在前面幾天,我們探討了記憶體、閉包、高階函數、非同步與 Event Loop。今天我們要來拆解 React 開發者每天都在寫、卻最容易被誤解的語法——JSX。
瀏覽器完全看不懂 JSX。你在 React 專案中寫的 JSX,在編譯打包階段,都會被 Babel(或 SWC、Vite 等編譯工具)轉譯成純粹的 JavaScript 函數呼叫。
例如:
const element = (
<h1 className="title" id="heading">
Hello React!
</h1>
);
經過 Babel 編譯後,會變成真正的 JavaScript:
// 舊版轉譯結果 (React 17 以前)
const element = React.createElement(
'h1',
{ className: 'title', id: 'heading' },
'Hello React!'
);
React.createElement 的三個參數:
React.createElement() 執行後,會回傳一個純粹的 JavaScript 物件,這就是 React Element(虛擬 DOM 節點的前身)。
如果你把 element 變數 console.log 印出來,會看到類似這樣的結構:
// 真正的 React Element 物件結構
{
$$typeof: Symbol(react.element), // 常用於安全性防止 XSS 攻擊
type: 'h1',
props: {
className: 'title',
id: 'heading',
children: 'Hello React!' // 子節點會放在 props.children
},
ref: null,
key: null
}
React 的工作,就是接收這個龐大的 JavaScript 物件樹(Virtual DOM),最後再交由 react-dom 渲染成真正的瀏覽器 DOM 節點。
當你明白「JSX 就是 React.createElement() 函數呼叫」後,React 許多看似無理的語法限制瞬間變得合情合理:
// ❌ 錯誤:無法回傳兩個獨立根節點
return (
<h1>Title</h1>
<p>Content</p>
);
// 等同於回傳兩個未封裝的函數呼叫,在 JS 中是語法錯誤 (SyntaxError):
// return React.createElement('h1') React.createElement('p');
解法:使用 <React.Fragment> 或簡寫 <> ... </> 包裹,這相當於建立一個容器來盛裝這組子元素。
為什麼屬性名稱是 className 而不是 class?
因為 JSX 最終會轉譯為 JS 物件,而在 ES6 中,class 是 JavaScript 的保留字 (Reserved Word)(用於定義類別)。為了避免語法衝突與解析爭議,React 採用 DOM Property 的命名習慣(如 className、htmlFor)。
為什麼大括號 {} 裡面只能放「運算式 (Expression)」,不能放「語句 (Statement)」?
// ❌ 錯誤:不能在 {} 寫 if 語句
<div>{ if (isLoggedIn) { return <User />; } }</div>
// ✅ 正確:可以使用三元運算子 (運算式會回傳值)
<div>{isLoggedIn ? <User /> : <Guest />}</div>
原因:因為 {} 裡面的內容會直接被充當 React.createElement() 的參數。JavaScript 函數的參數位置只能放入有傳回值的「運算式」,不能放入如 if、for 或 switch 等「語句」。